package com.cnap.multiclouldmgmt.service.impl;

import com.cnap.multiclouldmgmt.MulticlouldmgmtApplicationTests;
import com.cnap.multiclouldmgmt.model.RepByPage;
import com.cnap.multiclouldmgmt.model.exception.JsonException;
import com.cnap.multiclouldmgmt.model.k8sModels.NodeModel;
import com.cnap.multiclouldmgmt.model.scale.QueryScalingConfigParam;
import com.cnap.multiclouldmgmt.model.scale.ScalingConfigDTO;
import com.cnap.multiclouldmgmt.model.scale.db.ScalingConfigPO;
import com.cnap.multiclouldmgmt.model.scale.db.ScalingGroupPO;
import com.cnap.multiclouldmgmt.model.scale.db.ScalingPolicyPO;
import com.cnap.multiclouldmgmt.model.scale.db.ScalingResourcePO;
import com.cnap.multiclouldmgmt.model.scale.group.QueryScalingGroupParam;
import com.cnap.multiclouldmgmt.model.scale.group.ScalingGroupDTO;
import com.cnap.multiclouldmgmt.model.scale.group.ScalingGroupStatusEnum;
import com.cnap.multiclouldmgmt.model.scale.group.ScalingLabelEnum;
import com.cnap.multiclouldmgmt.model.scale.policy.ScalingPolicyDTO;
import com.cnap.multiclouldmgmt.repository.master.ScalingConfigMapper;
import com.cnap.multiclouldmgmt.repository.master.ScalingGroupMapper;
import com.cnap.multiclouldmgmt.repository.master.ScalingPolicyMapper;
import com.cnap.multiclouldmgmt.repository.master.ScalingResourceMapper;
import com.cnap.multiclouldmgmt.service.K8sClusterService;
import com.cnap.multiclouldmgmt.service.ScalingService;
import com.cnap.multiclouldmgmt.util.TokenUtils;
import com.cnap.multiclouldmgmt.vm.web.VMScaleWeb;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * <类文件描述>
 *
 * @author xc
 **/
class ScalingServiceImplTest extends MulticlouldmgmtApplicationTests {

    @Autowired
    private ScalingService scalingService;

    @MockBean
    private ScalingConfigMapper configMapperMock;
    @MockBean
    private ScalingGroupMapper groupMapperMock;
    @MockBean
    private ScalingPolicyMapper policyMapperMock;
    @MockBean
    private ScalingResourceMapper resourceMapperMock;
    @MockBean
    private VMScaleWeb vmScaleWeb;
    @MockBean
    private K8sClusterService k8sClusterServiceMock;

    @BeforeAll
    static void setToken() {
        String s = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NjUyMjI3ODksInN1YiI6ImFkbWluIiwiaWF0IjoxNjY1MjA4Mzg5MzE1fQ._FxOBy7ChUuuSOYnAJOuK1S_IMdkH45VMC1Sd59bSUY";
        TokenUtils.setTOKEN(s);
    }

    @Test
    void findByPage() throws Exception {
        QueryScalingGroupParam param = new QueryScalingGroupParam();
        param.setPageNum(1);
        param.setPageSize(10);

        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setId("id");
        groupPO.setGroupName("bae");
        groupPO.setDeletePolicy("FIFO");
        groupPO.setClusterName("clusterName");
        groupPO.setLabel("label");
        groupPO.setConfigId("configId");
        groupPO.setExpectNum(2);
        groupPO.setMinNum(1);
        groupPO.setMaxNum(3);
        groupPO.setProvider("Huawei");

        ScalingPolicyPO policyPO = new ScalingPolicyPO();
        policyPO.setId("id");
        policyPO.setNum(1);
        policyPO.setType("type");
        policyPO.setContinuousNum(2);
        policyPO.setResource("cpu");
        policyPO.setCoolTime(3);
        policyPO.setGroupId("groupId");

        Mockito.when(groupMapperMock.selectByParamWithUser(param, "admin")).thenReturn(Collections.singletonList(groupPO));
        Mockito.when(policyMapperMock.selectPolicyByGroupId("id")).thenReturn(Collections.singletonList(policyPO));

        RepByPage<NodeModel> result = new RepByPage<>();
        result.setResult(Collections.emptyList());

        Mockito.when(k8sClusterServiceMock.getK8sClusterNodeInfosByConditions("Huawei", "clusterName",
                null, null, 1000, 1, null, "label")).thenReturn(result);

        Assertions.assertDoesNotThrow(() -> {
        });
        scalingService.findByPage(param);
    }

    @Test
    void createScalingGroupWithNoConfig() {
        ScalingGroupDTO scalingGroupDTO = new ScalingGroupDTO();
        scalingGroupDTO.setConfigId("configId");
        Mockito.when(configMapperMock.selectConfigById("configId")).thenReturn(null);

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.createScalingGroup(scalingGroupDTO);
        });
    }

    @Test
    void createScalingGroupWithNoPermission() {
        ScalingGroupDTO scalingGroupDTO = new ScalingGroupDTO();
        scalingGroupDTO.setConfigId("configId");


        ScalingConfigPO configPO = new ScalingConfigPO();
        configPO.setUserName("test");
        Mockito.when(configMapperMock.selectConfigById("configId")).thenReturn(configPO);

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.createScalingGroup(scalingGroupDTO);
        });
    }

    @Test
    void createScalingGroupWithMoreThan3() {
        ScalingGroupDTO scalingGroupDTO = new ScalingGroupDTO();
        scalingGroupDTO.setConfigId("configId");
        scalingGroupDTO.setLabel(ScalingLabelEnum.AI.getLabel());
        scalingGroupDTO.setClusterName("clusterName");


        ScalingConfigPO configPO = new ScalingConfigPO();
        configPO.setUserName("admin");
        Mockito.when(configMapperMock.selectConfigById("configId")).thenReturn(configPO);
        Mockito.when(groupMapperMock.countByLabelWithUserAndClusterName(ScalingLabelEnum.AI.getLabel(), "admin",
                "clusterName")).thenReturn(1);
        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.createScalingGroup(scalingGroupDTO);
        });
    }

    @Test
    void createScalingGroup() {
        ScalingGroupDTO scalingGroupDTO = new ScalingGroupDTO();
        scalingGroupDTO.setConfigId("configId");
        scalingGroupDTO.setLabel(ScalingLabelEnum.AI.getLabel());
        scalingGroupDTO.setClusterName("clusterName");
        scalingGroupDTO.setProvider("pro");
        scalingGroupDTO.setDeletePolicy("delete-policy");
        scalingGroupDTO.setGroupName("gpname");
        scalingGroupDTO.setMaxNum(3);
        scalingGroupDTO.setMinNum(1);
        scalingGroupDTO.setExpectNum(2);

        ScalingConfigPO configPO = new ScalingConfigPO();
        configPO.setUserName("admin");
        Mockito.when(configMapperMock.selectConfigById("configId")).thenReturn(configPO);
        Mockito.when(groupMapperMock.countByLabelWithUserAndClusterName(ScalingLabelEnum.AI.getLabel(), "admin",
                "clusterName")).thenReturn(0);
        Assertions.assertDoesNotThrow(() -> {
            scalingService.createScalingGroup(scalingGroupDTO);
        });
    }

    @Test
    void startScaleWithNoGroup() {
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(null);

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.startScale("gpId");
        });
    }

    @Test
    void startScaleWithAlreadyStart() {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(1);
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);
        Mockito.when(groupMapperMock.updateGroupStatusById("gpId", ScalingGroupStatusEnum.START.getStatus())).thenReturn(1);

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.startScale("gpId");
        });
    }

    @Test
    void startScale() {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(0);
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);
        Mockito.when(groupMapperMock.updateGroupStatusById("gpId", ScalingGroupStatusEnum.START.getStatus())).thenReturn(1);

        ScalingPolicyPO policyPO = new ScalingPolicyPO();
        policyPO.setId("poId");
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(policyPO));

        Assertions.assertDoesNotThrow(() -> {
            scalingService.startScale("gpId");
        });
    }

    @Test
    void startScaleWithError() throws InterruptedException {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(0);
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);
        Mockito.when(groupMapperMock.updateGroupStatusById("gpId", ScalingGroupStatusEnum.START.getStatus())).thenReturn(1);

        ScalingPolicyPO policyPO = new ScalingPolicyPO();
        policyPO.setId("poId");
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(policyPO));

        Mockito.when(vmScaleWeb.addVmPolicy("poId")).thenThrow(new InterruptedException());

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.startScale("gpId");
        });
    }

    @Test
    void stopScaleWithNoGroup() {
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(null);
        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.startScale("gpId");
        });
    }

    @Test
    void stopScaleWithAlreadyStop() {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(ScalingGroupStatusEnum.STOP.getStatus());
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);
        Assertions.assertDoesNotThrow(() -> {
            scalingService.stopScale("gpId");
        });
    }

    @Test
    void stopScaleWithError() throws InterruptedException {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(ScalingGroupStatusEnum.START.getStatus());
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);

        ScalingPolicyPO policyPO = new ScalingPolicyPO();
        policyPO.setId("poId");
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(policyPO));

        Mockito.when(vmScaleWeb.deleteVmPolicy("poId")).thenThrow(new InterruptedException());

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.stopScale("gpId");
        });
    }

    @Test
    void stopScale() throws InterruptedException {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(ScalingGroupStatusEnum.START.getStatus());
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);

        ScalingPolicyPO policyPO = new ScalingPolicyPO();
        policyPO.setId("poId");
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(policyPO));

        Mockito.when(vmScaleWeb.deleteVmPolicy("poId")).thenReturn(null);

        Assertions.assertDoesNotThrow(() -> {
            scalingService.stopScale("gpId");
        });
    }

    @Test
    void deleteScalingGroupWithNoGroup() {
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(null);

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.deleteScalingGroup("gpId", true);
        });
    }

    @Test
    void deleteScalingGroupWithAlreadyStart() {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(1);
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.deleteScalingGroup("gpId", true);
        });
    }


    @Test
    void deleteScalingGroupWithDeletePolicyError() {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(0);
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);

        ScalingPolicyPO policyPO = new ScalingPolicyPO();
        policyPO.setId("poId");
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(policyPO));
        Mockito.when(policyMapperMock.deletePolicyByGroupId("gpId")).thenReturn(0);

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.deleteScalingGroup("gpId", true);
        });
    }

    @Test
    void deleteScalingGroupWithDeleteGroupError() {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(0);
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);

        ScalingPolicyPO policyPO = new ScalingPolicyPO();
        policyPO.setId("poId");
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(policyPO));
        Mockito.when(policyMapperMock.deletePolicyByGroupId("gpId")).thenReturn(1);
        Mockito.when(groupMapperMock.deleteGroupById("gpId")).thenReturn(0);
        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.deleteScalingGroup("gpId", true);
        });
    }

    @Test
    void deleteScalingGroup() {
        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(0);
        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(groupPO);

        ScalingPolicyPO policyPO = new ScalingPolicyPO();
        policyPO.setId("poId");
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(policyPO));
        Mockito.when(policyMapperMock.deletePolicyByGroupId("gpId")).thenReturn(1);
        Mockito.when(groupMapperMock.deleteGroupById("gpId")).thenReturn(1);
        Assertions.assertDoesNotThrow(() -> {
            scalingService.deleteScalingGroup("gpId", true);
        });
    }

    @Test
    void createScalingPolicyWith3Dtos() {
        ScalingPolicyDTO scalingPolicyDTO = new ScalingPolicyDTO();
        ScalingPolicyDTO scalingPolicyDTO1 = new ScalingPolicyDTO();
        ScalingPolicyDTO scalingPolicyDTO2 = new ScalingPolicyDTO();
        List<ScalingPolicyDTO> list = Arrays.asList(scalingPolicyDTO1, scalingPolicyDTO, scalingPolicyDTO2);
        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.createScalingPolicy(list);
        });
    }

    @Test
    void createScalingPolicyWith2SameDto() {
        ScalingPolicyDTO scalingPolicyDTO = new ScalingPolicyDTO();
        scalingPolicyDTO.setType("min");
        ScalingPolicyDTO scalingPolicyDTO1 = new ScalingPolicyDTO();
        scalingPolicyDTO1.setType("min");
        List<ScalingPolicyDTO> list = Arrays.asList(scalingPolicyDTO1, scalingPolicyDTO);
        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.createScalingPolicy(list);
        });
    }

    @Test
    void createScalingPolicyWithNoGroup() {
        ScalingPolicyDTO scalingPolicyDTO = new ScalingPolicyDTO();
        scalingPolicyDTO.setType("min");
        scalingPolicyDTO.setGroupId("gpId");
        List<ScalingPolicyDTO> list = Collections.singletonList(scalingPolicyDTO);

        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(null);
        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.createScalingPolicy(list);
        });
    }

    @Test
    void createScalingPolicyWithDeletePolicyError() {
        ScalingPolicyDTO scalingPolicyDTO = new ScalingPolicyDTO();
        scalingPolicyDTO.setType("min");
        scalingPolicyDTO.setGroupId("gpId");
        List<ScalingPolicyDTO> list = Collections.singletonList(scalingPolicyDTO);

        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(new ScalingGroupPO());
        ScalingPolicyPO po = new ScalingPolicyPO();
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(po));
        Mockito.when(policyMapperMock.deletePolicyByGroupId("gpId")).thenReturn(0);
        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.createScalingPolicy(list);
        });
    }

    @Test
    void createScalingPolicy() {
        ScalingPolicyDTO scalingPolicyDTO = new ScalingPolicyDTO();
        scalingPolicyDTO.setType("min");
        scalingPolicyDTO.setGroupId("gpId");
        List<ScalingPolicyDTO> list = Collections.singletonList(scalingPolicyDTO);

        Mockito.when(groupMapperMock.selectGroupById("gpId")).thenReturn(new ScalingGroupPO());
        ScalingPolicyPO po = new ScalingPolicyPO();
        Mockito.when(policyMapperMock.selectPolicyByGroupId("gpId")).thenReturn(Collections.singletonList(po));
        Mockito.when(policyMapperMock.deletePolicyByGroupId("gpId")).thenReturn(1);
        Assertions.assertDoesNotThrow(() -> {
            scalingService.createScalingPolicy(list);
        });
    }

    @Test
    void testFindByPage() {
        QueryScalingConfigParam param = new QueryScalingConfigParam();
        param.setPageNum(1);
        param.setPageSize(10);

        ScalingConfigPO configPO = new ScalingConfigPO();
        configPO.setId("id");
        configPO.setCreateTime(LocalDateTime.now());
        configPO.setCloudResourceIds(Collections.singletonList("resId"));
        configPO.setConfigName("cfgName");

        Mockito.when(configMapperMock.selectByParam(param, "admin")).thenReturn(Collections.singletonList(configPO));

        Assertions.assertDoesNotThrow(() -> {
            scalingService.findByPage(param);
        });
    }

    @Test
    void createScalingConfig() {
        ScalingConfigDTO scalingConfigDTO = new ScalingConfigDTO();
        scalingConfigDTO.setConfigName("name");
        scalingConfigDTO.setProvider("pro");
        scalingConfigDTO.setCloudResourceIds("reId1");

        Mockito.when(configMapperMock.insertScalingConfig(Mockito.any(ScalingConfigPO.class))).thenReturn(1);

        boolean result = scalingService.createScalingConfig(scalingConfigDTO);

        Assertions.assertTrue(result);
    }

    @Test
    void deleteScalingConfigByIdWithNoConfig() {
        Mockito.when(configMapperMock.selectConfigById("configId")).thenReturn(null);

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.deleteScalingConfigById("configId");
        });
    }

    @Test
    void deleteScalingConfigByIdWithConfigUsed() {
        Mockito.when(configMapperMock.selectConfigById("configId")).thenReturn(new ScalingConfigPO());

        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(1);
        Mockito.when(groupMapperMock.selectByConfigId("configId")).thenReturn(Collections.singletonList(groupPO));

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.deleteScalingConfigById("configId");
        });
    }

    @Test
    void deleteScalingConfig() {
        Mockito.when(configMapperMock.selectConfigById("configId")).thenReturn(new ScalingConfigPO());

        ScalingGroupPO groupPO = new ScalingGroupPO();
        groupPO.setStatus(0);
        Mockito.when(groupMapperMock.selectByConfigId("configId")).thenReturn(Collections.singletonList(groupPO));
        Mockito.when(configMapperMock.deleteConfigById("configId")).thenReturn(1);

        boolean result = scalingService.deleteScalingConfigById("configId");
        Assertions.assertTrue(result);

    }

    @Test
    void getAllCloudResources() {
        Mockito.when(resourceMapperMock.selectAll()).thenReturn(Collections.emptyList());

        Assertions.assertDoesNotThrow(() -> {
        });
        scalingService.getAllCloudResources();
    }

    @Test
    void getCloudResourceById() {
        Mockito.when(resourceMapperMock.selectResourceById("resId")).thenReturn(new ScalingResourcePO());

        Assertions.assertDoesNotThrow(() -> {
            scalingService.getCloudResourceById("redId");
        });

    }

    @Test
    void createDefaultScalingGroupForNewClusterWithExist() {
        Mockito.when(groupMapperMock.selectGroupByClusterName("clusterName")).thenReturn(Collections.singletonList(new ScalingGroupPO()));

        Assertions.assertDoesNotThrow(() -> {
            scalingService.createDefaultScalingGroupForNewCluster("admin", "clusterName", "pro", 2);
        });
    }

    @Test
    void createDefaultScalingGroupForNewCluster() {

        Assertions.assertThrows(JsonException.class, () -> {
            scalingService.createDefaultScalingGroupForNewCluster("admin", "clusterName", "pro", 2);
        });
    }
}